/*
Linux提供了复杂的存储管理系统，使得进程所能访问的内存达到4GB

进程的4GB内存空间被人为的分为两个部分--用户空间与内核空间。用户空间地址分布从0到3GB(PAGE_OFFSET，在0x86中它等于0xC0000000)，3GB到4GB为内核空间

内核空间中，从3G到vmalloc_start这段地址是物理内存映射区域（该区域中包含了内核镜像、物理页框表mem_map等等），比如我们使用 的VMware虚拟系统内存是256M，那么3G～3G+256M这片内存就应该映射物理内存。在物理内存映射区之后，就是vmalloc区域。对于256M的系统而言，vmalloc_start位置应在3G+256M附近（在物理内存映射区与vmalloc_start期间还存在一个8M的gap 来防止跃界），vmalloc_end的位置接近4G(最后位置系统会保留一片128k大小的区域用于专用页面映射)

| 进程地址空间   | 物理内存映射区 |                                   |
0                   3G                     vmalloc start               vmalloc end

kmalloc和get_free_page申请的内存位于物理内存映射区域，而且在物理上也是连续的，它们与真实的物理地址只有一个固定的偏移，因此存在较简单的转换关系，virt_to_phys()可以实现内核虚拟地址转化为物理地址：
#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
extern inline unsigned long virt_to_phys(volatile void * address)
{
return __pa(address);
}



同理：
#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
extern inline void * phys_to_virt(unsigned long address)
{
return __va(address);
}


而vmalloc申请的内存则位于vmalloc_start～vmalloc_end之间，与物理地址没有简单的转换关系，虽然在逻辑上它们也是连续的，但是在物理上它们不要求连续。

再来介绍一下这三个函数：

1、 get_free_page:

 为分配页, 下列函数可用:
get_zeroed_page(unsigned int flags); 
返回一个指向新页的指针并且用零填充了该页.
__get_free_page(unsigned int flags); 
类似于 get_zeroed_page, 但是没有清零该页.
__get_free_pages(unsigned int flags, unsigned int order); 
分配并返回一个指向一个内存区第一个字节的指针, 内存区可能是几个(物理上连续)页长但是没有清零.
flags 参数同 kmalloc 的用法相同; 常常使用 GFP_KERNEL 或者 GFP_ATOMIC, 可能带有 __GFP_DMA 标志( 给可能用在 ISA DMA 操作的内存 ) 或者 __GFP_HIGHMEM 当可能使用高端内存时. [29]order 是你在请求的或释放的页数的以 2 为底的对数(即, log2N). 例如, 如果你要一个页 order 为 0, 如果你请求 8 页就是 3. 如果 order 太大(没有那个大小的连续区可用), 页分配失败. get_order 函数, 它使用一个整数参数, 可以用来从一个 size 中提取 order(它必须是 2 的幂)给主机平台. order 允许的最大值是 10 或者 11 (对应于 1024 或者 2048 页), 依赖于体系. 但是, 一个 order-10 的分配在除了一个刚刚启动的有很多内存的系统中成功的机会是小的.

2、vmalloc/kmalloc：

#include <linux/vmalloc.h> 
void *vmalloc(unsigned long size);
static inline void *kmalloc(size_t size, gfp_t flags)


我们用下面的程序来演示kmalloc、get_free_page和vmalloc的区别：
*/
#ifndef __KERNEL__
#define __KERNEL__
#endif

#ifndef MODULE
#define MODULE
#endif


#include <linux/module.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/init.h>

 


MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Fortune Cookie Kernel Module");
MODULE_AUTHOR("M. Tim Jones");

unsigned char *pagemem;
unsigned char *kmallocmem;
unsigned char *vmallocmem;

static int __init mem_module_init(void)
{
 //最好每次内存申请都检查申请是否成功
 //下面这段仅仅作为演示的代码没有检查
 if ((pagemem = (unsigned char*)__get_free_page(0))==NULL)
 {
  printk("get_free_page Error!");
  return -1;
 }
 printk("\n<1>pagemem addr=0x%08X\n", pagemem);
 
 if ((kmallocmem = (unsigned char*)kmalloc(100, 0))==NULL)
 {
  printk("kmalloc Error!");
  return -1;
 }
 printk("<2>kmallocmem addr=0x%08X\n", kmallocmem);
 
 if ((vmallocmem = (unsigned char*)vmalloc(1000000))==NULL)
 {
  printk("vmalloc Error!");
  return -1;
 }
 printk("<3>vmallocmem addr=0x%08X\n", vmallocmem);
 
 return 0;
}

static void __exit mem_module_exit(void)
{
 free_page(pagemem);
 kfree(kmallocmem);
 vfree(vmallocmem);
}

module_init(mem_module_init);
module_exit(mem_module_exit);
/*
编译： gcc -c -D__KERNEL__ -DMODULE v_km_getpage.c 
insmod

dmesg   结果：//256M = 0x10 000 000    所以pagemem /kmallocmem 应在0xc0 000 000--0xd0 000 000之间
						 18 000 000
pagemem addr=0xCB9BC000                             
kmallocmem addr=0xCE811BC0                        
vmallocmem addr=0xD08E1000                        

可以分析得出正如所料pagemem /kmallocmem 在0xc0 000 000--0xd0 000 000之间

vmallocmem >0xd0 000 000
*/
